package com.jim.director.framework.validation;

import com.jim.director.framework.annotation.Action;
import com.jim.director.framework.annotation.Actor;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Set;

/**
 * @Action 注解校验器
 * 校验Action注解的方法签名和使用规范
 *
 * @author Jim
 * @date 2024/10/5
 */
public class ActionValidator implements AnnotationValidator {

    @Override
    public ValidationResult validate(Class<?> clazz) {
        ValidationResult result = new ValidationResult(clazz.getSimpleName());

        // 只校验带有@Actor注解的类中的@Action方法
        if (!clazz.isAnnotationPresent(Actor.class)) {
            return result;
        }

        Method[] methods = clazz.getDeclaredMethods();
        Set<String> statusSet = new HashSet<>();
        boolean hasAction = false;

        for (Method method : methods) {
            if (method.isAnnotationPresent(Action.class)) {
                hasAction = true;
                Action action = method.getAnnotation(Action.class);

                // 1. 校验方法签名
                validateMethodSignature(method, result, clazz);

                // 2. 校验状态参数
                validateStatusParameter(action.status(), statusSet, result, clazz, method);

                // 3. 校验方法访问修饰符
                validateMethodModifiers(method, result, clazz);
            }
        }

        // 4. 校验Actor是否至少有一个Action
//        if (!hasAction) {
//            result.addWarning(String.format(
//                "类 %s 标注了 @Actor 注解，但没有任何 @Action 方法。" +
//                "Actor通常应该有至少一个行为方法。",
//                clazz.getSimpleName()
//            ));
//        }

        return result;
    }

    /**
     * 校验方法签名
     */
    private void validateMethodSignature(Method method, ValidationResult result, Class<?> clazz) {
        // 检查返回类型是否为void
        if (!method.getReturnType().equals(void.class)) {
            result.addError(String.format(
                "类 %s 的方法 %s 标注了 @Action 注解，但返回类型不是void。" +
                "Action方法必须返回void类型。请修改方法签名为: public void %s()",
                clazz.getSimpleName(), method.getName(), method.getName()
            ));
        }

        // 检查参数列表是否为空
        if (method.getParameterCount() > 0) {
            result.addError(String.format(
                "类 %s 的方法 %s 标注了 @Action 注解，但包含参数。" +
                "Action方法不能有参数。请修改方法签名为: public void %s()",
                clazz.getSimpleName(), method.getName(), method.getName()
            ));
        }

        // 检查是否抛出异常
        if (method.getExceptionTypes().length > 0) {
            result.addWarning(String.format(
                "类 %s 的方法 %s 标注了 @Action 注解，但声明了异常。" +
                "建议在方法内部处理异常，避免影响仿真流程。",
                clazz.getSimpleName(), method.getName()
            ));
        }
    }

    /**
     * 校验状态参数
     */
    private void validateStatusParameter(String status, Set<String> statusSet,
                                       ValidationResult result, Class<?> clazz, Method method) {
        if (status != null && !status.trim().isEmpty()) {
            // 检查状态值是否重复
            if (statusSet.contains(status)) {
                result.addError(String.format(
                    "类 %s 中存在重复的状态值 '%s'。方法 %s 的状态与其他Action冲突。" +
                    "同一个Actor中，每个状态值只能对应一个Action方法。",
                    clazz.getSimpleName(), status, method.getName()
                ));
            } else {
                statusSet.add(status);
            }

            // 检查状态值格式
            if (status.contains(" ")) {
                result.addWarning(String.format(
                    "类 %s 的方法 %s 的状态值 '%s' 包含空格。" +
                    "建议使用下划线或驼峰命名法，如: 'WALKING' 或 'walking'。",
                    clazz.getSimpleName(), method.getName(), status
                ));
            }

            if (!status.equals(status.toUpperCase()) && !status.equals(status.toLowerCase())) {
                result.addInfo(String.format(
                    "类 %s 的方法 %s 的状态值 '%s' 建议使用全大写（如'WALKING'）或全小写（如'walking'）格式。",
                    clazz.getSimpleName(), method.getName(), status
                ));
            }
        }
    }

    /**
     * 校验方法访问修饰符
     */
    private void validateMethodModifiers(Method method, ValidationResult result, Class<?> clazz) {
        // 检查访问修饰符
        if (Modifier.isPrivate(method.getModifiers())) {
            result.addError(String.format(
                "类 %s 的方法 %s 标注了 @Action 注解，但是private方法。" +
                "框架无法访问private方法。请将方法修改为public。",
                clazz.getSimpleName(), method.getName()
            ));
        }

        if (Modifier.isStatic(method.getModifiers())) {
            result.addError(String.format(
                "类 %s 的方法 %s 标注了 @Action 注解，但是static方法。" +
                "Action方法不能是静态方法，因为需要在Actor实例上调用。",
                clazz.getSimpleName(), method.getName()
            ));
        }

        if (Modifier.isAbstract(method.getModifiers())) {
            result.addError(String.format(
                "类 %s 的方法 %s 标注了 @Action 注解，但是abstract方法。" +
                "Action方法必须有具体实现。",
                clazz.getSimpleName(), method.getName()
            ));
        }

        if (Modifier.isFinal(method.getModifiers())) {
            result.addWarning(String.format(
                "类 %s 的方法 %s 标注了 @Action 注解，但是final方法。" +
                "虽然不影响功能，但可能限制框架的某些高级特性（如代理）。",
                clazz.getSimpleName(), method.getName()
            ));
        }
    }

    @Override
    public Class<?>[] getSupportedAnnotations() {
        return new Class<?>[]{Action.class};
    }

    @Override
    public String getValidatorName() {
        return "ActionValidator";
    }
}

